/*
	Copyright (c) 2011 Andrey O. Zbitnev (azbitnev@gmail.com)
	Licensed under the MIT License (LICENSE).

	$Id: ys.select.js 30 2012-07-26 12:11:26Z azbitnev@gmail.com $

	version: 0.6pa

	required modules: mousewheel, slider
*/

(function ($) {

	// private vars
	var installed = false,
		serial = 1,
		contexts = { }

	// class select
	$.ys.select = {

		create: function (selector, options) {

			var defaults = {
				select: 'text', // text | html
				scrollbars: 'vertical', // vertical | native | none
				action: 'click', // see 'popup'
				show: function ($ctl, $el) { $.ys.effects.showSlideDown($ctl) },
				hide: function ($ctl, $el) { $.ys.effects.hideSlideUp($ctl) },
				getItemContent: function ($opt) { return $opt.text() },
				empty: '<div class="message">list is empty...</div>',
				mousewheel: true,
				searchbox: false,
				searchboxMatcher: 'contains-all-words'
			}

			var searchboxMatchers = {

				'starts-with-phrase': function (filterText, itemText) { return itemText.toLowerCase().indexOf(filterText.toLowerCase()) == 0 },

				'contains-phrase': function (filterText, itemText) { return itemText.toLowerCase().indexOf(filterText.toLowerCase()) >= 0 },

				'contains-all-words': function (filterText, itemText) {
					var words = filterText.toLowerCase().replace(/[^a-z0-9а-я ]/gi, '').split(/\s+/)
					itemText = itemText.toLowerCase()
					var m = true
					for (var i = 0; i < words.length; i ++) {
						if (itemText.indexOf(words[i]) < 0) {
							m = false
							break
						}
					}
					return m
				}
			}

			var opts = $.extend(defaults, options);

			if (typeof opts.searchboxMatcher == 'string')
				opts.searchboxMatcher = searchboxMatchers[opts.searchboxMatcher]
			if (!opts.searchboxMatcher)
				opts.searchboxMatcher = function () { return true }

			return $(selector).each(function(i, el) {

				var $sel, $el = $(el)
				var firstSync = true

				$sel = $el.next()
				if ($sel.hasClass('ys-select')) {
					firstSync = false
				} else {
					var html = ''
						+ '<div class="ys-select">'
							+ '<div class="current ys-disclosure"><div></div></div>'
								+ '<div class="ys-list"><div>'
									+ '<div class="top"></div>'
									+ (opts.searchbox || $el.data('ys-select-searchbox') ? '<input type="text" class="ys-search field"/>' : '')
									+ '<div class="items"></div>'
									+ '<div class="right"></div>'
									+ '<div class="bottom"></div>'
							+ '</div></div>'
						+ '</div>'
					$el.css({ display: 'none' })
					$sel = $(html).insertAfter($el)
					var classes = $el.attr('class')
					if (classes)
						$sel.attr('class', $sel.attr('class') + ' ' + $el.attr('class'))
				}

				var $r = $sel.find('.right:eq(0)'), $l = $sel.find('.ys-list:eq(0)'), $i = $l.find('.items:eq(0)')

				if ($.browser.msie && $.browser.version <= 8) {
					$sel.addClass('ys-select-ie8')
					$l.addClass('ys-list-ie8')
				}

				if (opts.scrollbars == 'vertical') {
					var $vsb
					if (firstSync) {
						$vsb = $('<div class="ys-scrollbar-vertical"></div>')
						$r.append($vsb)
					} else {
						$vsb = $sel.find('.ys-scrollbar-vertical')
					}
					$.ys.slider.create($vsb, { orientation: 'vertical', onchange: function (val) {
						var items = $i.children('.item')
						var totalHeight = 0
						items.each(function () {
							var $ci = $(this)
							if ($ci.css('display') != 'none')
								totalHeight += $ci.outerHeight()
						})
						$i.scrollTop((totalHeight - $i.innerHeight()) * val)
					}})
					if (opts.action == 'click') {
						$vsb.click(function (evt) {
							evt.stopPropagation()
						})
					}
				} else if (!firstSync) {
					$i.addClass('ys-no-scrollbar-vertical')
				}
				if (firstSync && opts.scrollbars == 'native')
					$i.addClass('native')

				function invalidate() {

					var $cur = $sel.children('.current').children('div:eq(0)')
					var $opt = $sel.find('.selected:eq(0)')
					if ($opt.length <= 0)
						$opt = $sel.find('.item:eq(0)')
					if ($opt.length > 0) {
						if (opts.select == 'text') {
							$cur.text($opt.text())
						} else {
							$cur.html($opt.html())
						}
					} else {
						$cur.html('&nbsp;')
					}

					var $i = $sel.find('.items:eq(0)')
					var top = 0
					var item = $i.children('.selected')
					if (item.length > 0) {
						do {
							item = item.prev()
							if (item.length > 0)
								top += (item.css('display') == 'none' ? 0 : item.outerHeight())
						} while (item.length > 0)
						$i.scrollTop(top)
						top = $i.scrollTop()
					}

					var $vsb = $sel.find('.right > .ys-scrollbar-vertical')
					if ($vsb.length > 0) {
						var items = $i.children('.item')
						var visibleHeight = $i.innerHeight()
						var totalHeight = 0
						items.each(function () {
							var $ci = $(this)
							if ($ci.css('display') != 'none')
								totalHeight += $ci.outerHeight()
						})
						var $thumb = $vsb.children('.thumb')
						if (!$thumb.data('ys-min-height'))
							$thumb.data('ys-min-height', $thumb.height())
						var minHeight = $thumb.data('ys-min-height')
						var h = (visibleHeight * $vsb.innerHeight()) / totalHeight
						$thumb.height(h < minHeight ? minHeight : h)
						$vsb.val(top / (totalHeight - visibleHeight))
						if (visibleHeight < totalHeight) {
							$vsb.css({ display: 'block' })
							$i.removeClass('ys-no-scrollbar-vertical')
						} else {
							$vsb.css({ display: 'none' })
							$i.addClass('ys-no-scrollbar-vertical')
						}
					}
				}

				function selectItem($opt, raiseEvent) {
					var $list = $sel.find('.items')
					$list.children('.item').removeClass('selected')
					$opt.addClass('selected')
					invalidate()
					if (opts.action != 'click')
						$.ys.select.getContext($sel).hide()
					if (raiseEvent) {
						var val = $opt.data('ys-value')
						$el.children('option').each(function () {
							var $src = $(this)
							if ($src.attr('value') == val)
								$src.attr('selected', true)
						})
						$el.change()
					}
				}

				function getItem(val) {
					var $opt = $sel.find('.item:eq(0)')
					$sel.find('.item').each(function () {
						var $item = $(this)
						if (!$item.hasClass('ys-disabled') && $item.data('ys-value') == val)
							$opt = $item
					})
					return $opt
				}

				if (opts.searchbox || $el.data('ys-select-searchbox')) {
					$sel.find('input').click(function (evt) {
						evt.preventDefault()
						evt.stopPropagation()
						return false
					}).bind(($.browser.msie && $.browser.version <= 8 ? 'propertychange' : 'input'), function (evt) {
						var filterText = $(this).val()
						$sel.find('.item').each(function () {
							var $item = $(this)
							$item.css({ display: (opts.searchboxMatcher(filterText, $item.text()) ? 'block' : 'none') })
						})
						invalidate()
					})
				}

				$.ys.popup.create($sel, {
					action: opts.action,
					show: opts.show,
					hide: opts.hide,
					popup: '.ys-list'
				})

				if (opts.mousewheel) {
					$sel.find('.ys-list').mousewheel(function (evt, delta, deltaX, deltaY) {
						var $vsb = $sel.find('.right > .ys-scrollbar-vertical')
						var val = $vsb.val()
						if (deltaY < 0) {
							val += 0.05
						} else {
							val -= 0.05
						}
						if (val < 0) val = 0
						if (val > 1) val = 1
						$vsb.val(val)
					})
					
				}

				function setEventHandlers() {
					$sel.find('.item').each(function () {
						var $opt = $(this)
						if ($opt.hasClass('ys-disabled')) {
							if (opts.action == 'click')
								$opt.mouseup(function () {
									$sel.children('.ys-list').css({ visibility: 'hidden' })
								})
						} else {
							$opt.click(function () { selectItem($opt, true) })
						}
					})
				}

				var ctx = {

					sync: function () {
						if ($el.attr('disabled')) {
							if (!$sel.hasClass('ys-disabled'))
								$sel.addClass('ys-disabled')
						} else {
							$sel.removeClass('ys-disabled')
						}
						var $list = $sel.find('.items')
						$list.html('<!-- -->')
						$el.find('option').each(function () {
							var $src = $(this)
							var $opt = $('<div class="item"></div>')
							$opt.html(opts.getItemContent($src))
							$opt.data('ys-value', $src.attr('value'))
							if ($src.attr('disabled')) {
								$opt.addClass('ys-disabled')
							}
							if ($src.attr('selected')) {
								$list.children('.item').removeClass('selected')
								$opt.addClass('selected')
							}
							if ($.browser.msie && $.browser.version <= 8) {
								$opt.hover(
									function () { $opt.addClass('hovered') },
									function () { $opt.removeClass('hovered') }
								)
							}
							$list.append($opt)
						})
						if ($list.children('.item').length <= 0)
							$list.html(opts.empty)
						setEventHandlers()
						invalidate()
					},

					get: function () {
						return $sel.find('.selected').data('ys-value')
					},

					set: function (val) {
						if (this.get() != val)
							selectItem(getItem(val), true)
					}
				}

				var id = (serial ++)
				contexts[id] = ctx
				$el.data('ys-select-id', id)
				$sel.data('ys-select-id', id)

				$(document).keydown(function (evt) {
					if ($sel.find('.ys-list').css('visibility') == 'visible') {
						if (evt.keyCode == 38) {
							var p = $sel.find('.selected').prev()
							while (p.length > 0) {
								if (!p.hasClass('ys-disabled')) {
									selectItem(p, true)
									break
								}
								p = p.prev()
							}
						} else if (evt.keyCode == 40) {
							var n = $sel.find('.selected').next()
							while (n.length > 0) {
								if (!n.hasClass('ys-disabled')) {
									selectItem(n, true)
									break
								}
								n = n.next()
							}
						}
					}
				})

				if (firstSync) {
					ctx.sync()
				} else {
					setEventHandlers()
//					invalidate()
				}

				$el.change(function () {
					var val = $el.children('option[selected]').attr('value')
					if (ctx.get() != val)
						selectItem(getItem(val), false)
				})

				/*setTimeout(function () {
					if (firstSync) {
						ctx.sync()
					} else {
						invalidate()
					}
				}, 250)*/
			})
		},

		getContext: function (selector) {
			var id = $(selector).data('ys-select-id')
			return contexts[id]
		}

	}

})(jQuery);
